package com.wdcg.view;

import java.util.ArrayList;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.LinearGradient;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.graphics.Path;
import android.graphics.RectF;
import android.graphics.Shader;
import android.location.Location;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;

import com.wdcg.R;
import com.wdcg.activity.MainActivity;
import com.wdcg.model.access.BonusAccess;
import com.wdcg.model.dao.BonusDescription;
import com.wdcg.utils.GameLogic;

/**
 * @author Grzegorz Matyszczak<BR>
 * 
 */
public class RadarView extends View {

	private static final float RADAR_ANGLE = -30f;
	private static final float ROTATION_STEP = 2;
	private static final int UPDATE_DELAY = 30;


	private List<RadarPoint> radarPoints;
	private Location currentLocation;	
	private int width;
	private int height;
	private int radarRadius;
	private float rotation;
	private float compassRotation;
	private Paint radarPaint;
	private Path path;
	private RectF oval;
	private Timer timer;
	private TimerTask task;
	public boolean isTimerCanceled;

	private Bitmap targetBitmap;
	private int clickableLimit;

	private MainActivity context;

	/**
	 * @param context
	 * @param attrs
	 * @param defStyleAttr
	 */
	public RadarView(Context context, AttributeSet attrs, int defStyleAttr) {
		super(context, attrs, defStyleAttr);
		init(context);
	}

	/**
	 * @param context
	 * @param attrs
	 */
	public RadarView(Context context, AttributeSet attrs) {
		super(context, attrs);
		init(context);
	}

	/**
	 * @param context
	 */
	public RadarView(Context context) {
		super(context);
		init(context);
	}

	private void init(Context context) {
		radarPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
		radarPaint.setColor(getResources().getColor(R.color.radar_line));
		radarPaint.setStyle(Style.FILL);

		radarPoints = new ArrayList<RadarView.RadarPoint>();

		rotation = 0;

		path = new Path();
		oval = new RectF();

		targetBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.target);
		clickableLimit = getResources().getInteger(R.integer.bonus_clickable_limit);

		this.context = (MainActivity)context;
	}

	@Override
	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
		super.onMeasure(widthMeasureSpec, heightMeasureSpec);
		width = MeasureSpec.getSize(widthMeasureSpec);
		height = MeasureSpec.getSize(heightMeasureSpec);

		int lineSpacePortrait = (int) (width*RadarBackgroundView.BACKGROUND_LINE_SPACE);
		int lineSpaceLandscape = (int) (height*RadarBackgroundView.BACKGROUND_LINE_SPACE);
		radarRadius = width < height ? width/2-(width/2-lineSpacePortrait/2)%lineSpacePortrait : height/2-(height/2-lineSpaceLandscape/2)%lineSpaceLandscape;

		if(radarPaint != null) {
			for(RadarPoint point : radarPoints) {
				point.calculateCoordinates();
				point.calculateLimitAngle(radarRadius, 0);
			}
		}

		path.moveTo(width/2, height/2);
		oval.set(width/2-radarRadius, height/2-radarRadius, width/2+radarRadius, height/2+radarRadius);
		path.arcTo(oval, 0, RADAR_ANGLE);

		float x = width/2+radarRadius/2;
		float y = height/2;
		Matrix rotateMatrix = new Matrix();
		rotateMatrix.setRotate(RADAR_ANGLE, width/2, height/2);
		float[] point = new float[]{x, y};
		rotateMatrix.mapPoints(point);
		int xr = (int) point[0];
		int yr = (int) point[1];

		radarPaint.setShader(new LinearGradient(x, y, xr, yr, 0xff0b7706, 0x002b7428, Shader.TileMode.CLAMP));

	}

	@Override
	protected void onDraw(Canvas canvas) {
		super.onDraw(canvas);
		if(currentLocation != null) {
			//radar points
			for(RadarPoint point : radarPoints) {
				if(point.isOnRadar) {
					float limitAngle = (point.limitAngle+compassRotation)%360;
					if(point.isFadingOut) {
						canvas.rotate(point.fixedRotation, canvas.getWidth()/2, canvas.getHeight()/2);
						point.draw(canvas);
						canvas.rotate(-point.fixedRotation, canvas.getWidth()/2, canvas.getHeight()/2);
					}
					else if(limitAngle < rotation && limitAngle > rotation-10)
						point.fadeOut(compassRotation);
				}
			}
		}

		//transform canvas
		canvas.rotate(rotation, canvas.getWidth()/2, canvas.getHeight()/2);

		//radar needle
		canvas.drawPath(path, radarPaint);		
	}

	public void startTimer() {
		timer = new Timer();
		task = new TimerTask() {

			@Override
			public void run() {
				rotation = (rotation+ROTATION_STEP)%360;
				postInvalidate();
			}
		};
		timer.schedule(task, 0, UPDATE_DELAY);
		isTimerCanceled = false;
	}

	public void stopTimer() {
		task.cancel();
		for(RadarPoint point : radarPoints) {
			if(point.isFadingOut) {
				point.fadingTask.cancel();
				point.isFadingOut = false;
			}
		}
		timer.cancel();
		isTimerCanceled = true;
	}

	public void setRadarPoints(List<BonusDescription> bonuses) {
		radarPoints = new ArrayList<RadarPoint>();
		for (BonusDescription bonus : bonuses) {
			radarPoints.add(new RadarPoint(bonus.getId(), bonus.getBonus()
					.getLatitude(), bonus.getBonus().getLongitude()));
		}
	}

	public void setCurrentLocation(Location location) {
		currentLocation = location;
		for(RadarPoint point : radarPoints) {
			point.calculateCoordinates();
			point.calculateLimitAngle(radarRadius, 0);
		}
	}

	/**
	 * @param degrees
	 */
	public void setRadarRotation(float degrees) {
		compassRotation = (degrees+360)%360;
	}

	@Override
	public boolean onTouchEvent(MotionEvent event) {
		if(event.getAction() == MotionEvent.ACTION_UP) {
			for(RadarPoint point : radarPoints) {
				if(point.isClickable && point.isFadingOut) {
					Matrix rotateMatrix = new Matrix();
					rotateMatrix.setRotate(point.fixedRotation, width/2, height/2);
					float[] coords = new float[]{point.x, point.y};
					rotateMatrix.mapPoints(coords);

					if(event.getX() >= coords[0]-targetBitmap.getWidth()/2 && event.getX() <= coords[0]+targetBitmap.getWidth()/2 &&
							event.getY() >= coords[1]-targetBitmap.getHeight()/2 && event.getY() <= coords[1]+targetBitmap.getHeight()/2) {
						BonusAccess.setRandomBonusTypeForBonusDescription(BonusAccess.getBonusDescriptionById(point.bonusDescriptionId));
						GameLogic.handleBonusActivation(context, point.bonusDescriptionId, currentLocation);
						((MainActivity)context).showBonusInfo(point.bonusDescriptionId);
					}
				}
			}
		}
		return true;
	}

	private class RadarPoint {

		private static final int POINT_RADIUS = 10;
		private static final int FADE_OUT_PERIOD = 50;
		private static final int FADE_OUT_ALPHA_STEP = 5;

		private int bonusDescriptionId;
		private double latitude;
		private double longitude;

		private int x;
		private int y;
		private float limitAngle;
		private Paint radarPointPaint;
		private boolean isFadingOut;
		private float fixedRotation;
		private boolean isClickable;

		private boolean isOnRadar;

		private TimerTask fadingTask;

		public RadarPoint(int bonusDescriptionId, double latitude,
				double longitude) {
			this.bonusDescriptionId = bonusDescriptionId;
			this.latitude = latitude;
			this.longitude = longitude;
			radarPointPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
			radarPointPaint.setColor(Color.RED);
			isFadingOut = false;
			isOnRadar = false;
		}

		public void calculateCoordinates() {
			if(currentLocation != null) {
				Location location = new Location("FAKE");
				location.setLatitude(latitude);
				location.setLongitude(longitude);
				float bearing = currentLocation.bearingTo(location);
				float distance = currentLocation.distanceTo(location);

				x = width/2;
				y = (int) (height/2 - (float)radarRadius/getResources().getInteger(R.integer.radar_background_range)*distance);
				isOnRadar = (y >= height/2 - radarRadius);		
			
				Matrix rotateMatrix = new Matrix();
				rotateMatrix.setRotate(bearing, width/2, height/2);
				float[] point = new float[]{x, y};
				rotateMatrix.mapPoints(point);
				x = (int) point[0];
				y = (int) point[1];

				isClickable = (distance <= clickableLimit);
			}
		}


		public void fadeOut(float compassRotation) {
			if(!isTimerCanceled) { 
				fixedRotation = compassRotation;
				isFadingOut = true;
				fadingTask = new TimerTask() {

					@Override
					public void run() {
						int alpha = radarPointPaint.getAlpha();
						if(alpha > 0) {
							radarPointPaint.setAlpha(alpha-FADE_OUT_ALPHA_STEP);
							postInvalidate();
						}
						else {
							radarPointPaint.setAlpha(255);
							cancel();
							isFadingOut = false;
						}
					}
				};
				timer.schedule(fadingTask, FADE_OUT_PERIOD, FADE_OUT_PERIOD);
			}
		}

		public void calculateLimitAngle(int vecX, int vecY) {			
			int vec2X = x-width/2;
			int vec2Y = y-height/2;
			double cos = (double)(vecX*vec2X+vecY*vec2Y)/(Math.sqrt(vecX*vecX+vecY*vecY)*Math.sqrt(vec2X*vec2X+vec2Y*vec2Y));
			limitAngle = (float) Math.toDegrees(Math.acos(cos));
			if(y < height/2)
				limitAngle = 360 - limitAngle;
		}

		public void draw(Canvas canvas) {
			if(isClickable)
				canvas.drawBitmap(targetBitmap, x-targetBitmap.getWidth()/2, y-targetBitmap.getHeight()/2, radarPointPaint);
			else
				canvas.drawCircle(x, y, POINT_RADIUS, radarPointPaint);
		}

	}

}
